-- card: 10251 from stack: in.3 -- bmap block id: 16119 -- flags: 4000 -- background id: 16896 -- name: MultiSort ----- HyperTalk script ----- on opencard global nextPos hide card field "source" set the scroll of card field "documentation" to 0 hide button "arrow" put 0 into nextPos set the loc of btn arrow to "312,40" put empty into card field "sorted" pass opencard end opencard on Install get ChooseTargetStack() InstallResource XFCN,MultiSort,it end Install -- part 15 (field) -- low flags: 01 -- high flags: 0004 -- rect: left=322 top=199 right=302 bottom=485 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 9 -- style flags: 0 -- line height: 12 -- part name: sorted -- part 6 (button) -- low flags: 00 -- high flags: 8003 -- rect: left=304 top=303 right=325 bottom=425 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 1 -- font id: 0 -- text size: 12 -- style flags: 0 -- line height: 16 -- part name: Show C Source ----- HyperTalk script ----- on mouseUp get the visible of card field "source" set the visible of card field "source" to not it if it is false then set the name of me to "Hide C Source" else set the name of me to "Show C Source" end if end mouseUp -- part 9 (field) -- low flags: 01 -- high flags: 2007 -- rect: left=18 top=33 right=287 bottom=295 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 9 -- style flags: 0 -- line height: 12 -- part name: Documentation -- part 11 (button) -- low flags: 80 -- high flags: 0000 -- rect: left=303 top=29 right=51 bottom=321 -- title width / last selected line: 0 -- icon id / first selected line: 16560 / 16560 -- text alignment: 1 -- font id: 0 -- text size: 12 -- style flags: 0 -- line height: 16 -- part name: arrow -- part 13 (field) -- low flags: 01 -- high flags: 0004 -- rect: left=322 top=39 right=142 bottom=485 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 9 -- style flags: 0 -- line height: 12 -- part name: data -- part 14 (field) -- low flags: 01 -- high flags: 0004 -- rect: left=322 top=151 right=191 bottom=487 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 9 -- style flags: 0 -- line height: 12 -- part name: spec -- part 7 (field) -- low flags: 81 -- high flags: 0007 -- rect: left=18 top=31 right=290 bottom=489 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 10 -- style flags: 0 -- line height: 13 -- part name: Source -- part 12 (button) -- low flags: 00 -- high flags: A003 -- rect: left=85 top=298 right=320 bottom=185 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 1 -- font id: 0 -- text size: 12 -- style flags: 0 -- line height: 16 -- part name: Sort ----- HyperTalk script ----- on mouseUp put MultiSort(card field "data",card field "spec") into card field "sorted" end mouseUp -- part contents for card part 7 ----- text ----- /* MultiSort1.0d4.c */ /* © Trustees of Dartmouth College */ /* written in LightSpeed C © Think Technologies, Inc */ /* by Roger Brown 9/2/89 Courseware Development Group */ /* version 1.0d4 uses international string comparison */ /* version 1.0d3 first source release */ /* version 1.0d2: clean up the code, change type spec to whole words. */ /* version 1.0d1: numbers are handled in floating point */ /* Distribution version. Source is for THINK C version 4.0, but I still use the qksort routine from THINK LSC™ v. 3.0 because the v 4.0 _qsort doesn't seem to work. */ /* This is a HyperCard XFCN that sorts the lines of a HyperCard container according to the value of specified items in the line. It will do a multilevel sort on several items and can sort ascending or descending by alphabetic, numeric, time, or date order. There is no limit to the number of lines except for available memory. It uses the Lightspeed C quicksort function and it is quite fast! Only one pass is made over the data to find line starts. Case is ignored in this version. Syntax is: get MultiSort(container,specification) where container is any hypercard container (field, variable), presumed to be multi-lined. specification is a container with this format: for each item to sort on, in priority order, specify one line of three items: item number to sort on, sort type (alpha,numeric,date,time), and ascending or decending (a,d) returns: a copy of the container sorted by line or error messages. ex. get MultiSort(card field 1,spec) where spec is: 2,alpha,a item 2, alpahbetic order, ascending 3,numeric,d then item 3, numeric, descending Note on data formats: Time items must be in format hh:mm:ss (ex. 10:15:02). Seconds and hours are optional. MultiSort WILL NOT report improperly formatted times and WILL NOT correct for mis-specified time (ex. 9:99 the way that HyperCard can. If you are uncertain of time data, convert it to seconds in HyperTalk and sort it numerically. Date items must be in format mm/dd/yy (ex. 2/15/78). All three parts must be specified. MultiSort WILL NOT report improperly formatted dates and WILL NOT correct for mis-specified dates (ex. 1/44/50) the way that HyperCard can. If you are uncertain of date data, convert it to seconds in HyperTalk and sort it numerically. To compile: create a project with this, MacTraps, and the THINK LightSpeed C™ version 3.0 library source called qsort.c. (The THINK C™ version 4.0 code does not work with this XFCN in its present state.) and a file called C Utilities.c that you can generate from the C Utilities card of Dartmouth XCMD's. Build as code resource type XFCN named MultiSort, ID 2351. */ #include "MemoryMgr.h" #include "HyperXCmd.h" #include "sane.h" #include "XCmdGlue.inc.c" #include "SetUpA4.h" #define NULL 0L #define maxItems 32 #define alpha 1 #define numeric 2 #define time 3 #define date 4 #define ascend 1 #define descend 2 #define slash '/' #define colon ':' #define memoryError -1 /* marks a memory allocation error */ int *order; /* points to the line order array (in heap) */ Handle orderHandle; /* handle to the line order array */ int *lineStarts; /* points to array of line starts (in heap) */ Handle lsHandle; /* handle to the line starts array */ Handle theField; /* handle to the input copy of the container */ Handle theUCASEField; /* handle to an upper case version of container */ Handle DoSort(); /* the sorting routine */ int numSortFields; /* number of fields to sort on */ long sortItem[maxItems]; /* the number of the sort items */ char sortType[maxItems]; /* how to sort */ char sortDirection[maxItems]; /* which direction to sort */ long MyDateToSecs(); /* ------------------- utility functions ---------------------- */ /* change a string to all upper case */ ucase(s) char *s; { int i; char c; for (i=0;i 255 characters."); return; } if (c==(len-1)) { /* last item, no comma */ count = count+1; break; } } } if (count < i) strcpy(outStr,"Error: item out of range"); /* no item there */ else { temp[j] = 0; /* make it a C string */ strcpy(outStr,temp); /* move to output string */ } return; } /* how many HyperCard lines in string source */ NumHCLines(source) char *source; { int c; /* character counter */ int l; /* line counter */ int len; len = strlen(source); l = 0; for (c=0;clen) { /* out of range */ strcpy(dest,"Error: Line out of range."); return; } } c = 0; /* at line, start transfer */ for (j=i;jreturnValue = resultHandle; } /* check string for valid ascii and convert if ok. Return validity. Input string is changed to Pascal format */ char ValidStrToNum(s,n) char *s; long *n; { int c,len; /* length must be < 32768 */ len = strlen(s); if ((len<1)||(len>32768)) return FALSE; /* all characters must be 0..9,-,+ */ for (c=0;c57)) if ((s[c]!=45)&&(s[c]!=43)) return FALSE; } CtoPstr(s); StringToNum(s,n); return TRUE; } /* Get part of string "to" from characters "start" to "stop". Return it in string "from" */ SubString(to,from,start,stop) char *to,*from; int start,stop; { int i,j; j = 0; for (i=start;i num2) return 1; return 0; } /* Change a date in character format into a DateTimeRec format. */ ParseDate(tStr,dtr) char *tStr; DateTimeRec *dtr; { /* Input must be in mm/dd/yy format */ int delim[3],next = 0; char monthStr[16],dayStr[16],yearStr[16]; char *p; delim[0] = delim[1] = delim[2] = 0; p = tStr; while (1) { /* find the '/' delimeters */ if ((*p==slash)||(*p=='\0')) { delim[next] = (p-tStr); next++; } if (*p == '\0') break; p++; } SubString(monthStr,tStr,0,delim[0]); /* get the 3 parts */ SubString(dayStr,tStr,delim[0]+1,delim[1]); SubString(yearStr,tStr,delim[1]+1,delim[2]); dtr->month = CStrToInt(monthStr); /* convert */ dtr->day = CStrToInt(dayStr); dtr->year = CStrToInt(yearStr); } /* Compare two dates in DateTimeRec format. Return -1 if the first is later, 0 if they are equal, and 1 if the second is later. */ CompareDates(d1,d2) char *d1,*d2; { DateTimeRec dtr[2]; ParseDate(d1,&dtr[0]); ParseDate(d2,&dtr[1]); if (dtr[0].year > dtr[1].year) return 1; else if (dtr[0].year < dtr[1].year) return -1; if (dtr[0].month > dtr[1].month) return 1; else if (dtr[0].month < dtr[1].month) return -1; if (dtr[0].day > dtr[1].day) return 1; else if (dtr[0].day < dtr[1].day) return -1; return 0; } /* Change a time in character format into a DateTimeRec format. */ ParseTime(tStr,dtr) char *tStr; DateTimeRec *dtr; { /* Time must be in hh:mm:ss format. */ int delim[3],next = 0; char hourStr[16],minStr[16],secStr[16]; char *p; delim[0] = delim[1] = delim[2] = 0; /* find the ":" delimeters */ p = tStr; while (1) { if ((*p==colon)||(*p=='\0')) { delim[next] = (p-tStr); next++; } if (*p == '\0') break; p++; } SubString(hourStr,tStr,0,delim[0]); /* get the parts */ SubString(minStr,tStr,delim[0]+1,delim[1]); SubString(secStr,tStr,delim[1]+1,delim[2]); dtr->hour = CStrToInt(hourStr); /* convert */ dtr->minute = CStrToInt(minStr); dtr->second = CStrToInt(secStr); } /* Compare two tiems in DateTimeRec format. Return -1 if the first is later, 0 if they are equal, and 1 if the second is later. */ CompareTimes(t1,t2) char *t1,*t2; { DateTimeRec dtr[2]; ParseTime(t1,&dtr[0]); ParseTime(t2,&dtr[1]); if (dtr[0].hour > dtr[1].hour) return 1; else if (dtr[0].hour < dtr[1].hour) return -1; if (dtr[0].minute > dtr[1].minute) return 1; else if (dtr[0].minute < dtr[1].minute) return -1; if (dtr[0].second > dtr[1].second) return 1; else if (dtr[0].second < dtr[1].second) return -1; return 0; } /* compare two fields (items). Comparison depends on field type and the result is adjusted for ascending or descending specification */ CompareFields(f1,f2,type,dir) char *f1,*f2; /* string value of the 2 fields */ char type,dir; /* field type and direction */ { int result; int len1,len2; switch (type) { case alpha: len1 = strlen(f1); len2 = strlen(f2); result = IUMagString(f1,f2,len1,len2); break; case numeric: result = CompareNums(f1,f2); break; case date: result = CompareDates(f1,f2); break; case time: result = CompareDates(f1,f2); break; } if (dir == descend) result = -1*result; return result; } /* Compare line i and line j. This function is an input to qsort. */ static int compare(i,j) int i,j; { int f,result; Str255 item1,item2; for (f=0;fparamCount != 2) { strcpy(paramStr,"Not enough parameters in MultiSort."); ResultIs(paramPtr,paramStr); RestoreA4(); return; } /* get the input container copy */ MoveHHi(paramPtr->params[0]); HLock(paramPtr->params[0]); theField = (Handle)paramPtr->params[0]; /* get the specification container */ HLock(paramPtr->params[1]); strcpy(paramStr,*(paramPtr->params[1])); /* check bounds on sort items */ numSortFields = NumHCLines(paramStr); if (numSortFields > maxItems) { strcpy(paramStr,"MultiSort cannot handle more than 32 sort items."); ResultIs(paramPtr,paramStr); RestoreA4(); return; } /* check each specification line and remember the specs */ for (i=0;ireturnValue = DoSort(numSortFields); /* clean up */ HUnlock (paramPtr->params[0]); HUnlock (paramPtr->params[1]); RestoreA4(); return; } -- part contents for card part 9 ----- text ----- MultiSort version 1.0d4 Roger Brown This is a HyperCard XFCN that sorts the lines of a HyperCard container according to the value of specified items in the line. It will do a multilevel sort on several items and can sort ascending or descending by alphabetic, numeric, time, or date order. There is no limit to the number of lines except for available memory. It uses the Lightspeed C quicksort function and it is quite fast! Only one pass is made over the data to find line starts. Case is ignored in this version. Syntax is: get MultiSort(container,specification) where container is any hypercard container (field, variable), presumed to be multi-lined. specification is a container with this format: for each item to sort on, in priority order, specify one line of three items: item number to sort on, sort type (alpha,numeric,date,time), and ascending or descending (a,d) returns: a copy of the container sorted by line or error messages. ex. get MultiSort(card field 1,spec) where spec is: 2,alpha,a (item 2, alphabetic order, ascending) 3,numeric,d (then item 3, numeric, descending) Note on data formats: Time items must be in format hh:mm:ss (ex. 10:15:02). Seconds and hours are optional. MultiSort WILL NOT report improperly formatted times and WILL NOT correct for mis- specified time (ex. 9:99 the way that HyperCard can. If you are uncertain of time data, convert it to seconds in HyperTalk and sort it numerically. Date items must be in format mm/dd/yy (ex. 2/15/78). All three parts must be specified. MultiSort WILL NOT report improperly formatted dates and WILL NOT correct for mis-specified dates (ex. 1/44/50) the way that HyperCard can. If you are uncertain of date data, convert it to seconds in HyperTalk and sort it numerically. REVISION HISTORY 1.0d3 First public release. 1.0d4 uses international string comparison for alpha sorting. -- part contents for card part 13 ----- text ----- Ford, red, 1975, 4000 Chevy,red,1975,3500 Ford,white,1980,6000 Ford,red,1980,6500 Chevy,white,1985,8000 Buick,blue,1974,2000 Buick,blue,1974,2400 Ford,red,1980,6000 -- part contents for card part 14 ----- text ----- 1,alpha,a 3,date,a 4,numeric,d -- part contents for card part 15 ----- text ----- Buick,blue,1974,2400 Buick,blue,1974,2000 Chevy,red,1975,3500 Chevy,white,1985,8000 Ford, red, 1975, 4000 Ford,red,1980,6500 Ford,red,1980,6000 Ford,white,1980,6000